#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <semaphore.h>
#include <pthread.h>

// 该环形队列支持多生产者多消费者模型
// 三种关系：
// a: 生产者和消费者之间的互斥和同步
// b: 生产者之间的互斥访问
// c: 消费者之间的互斥访问
// 解决方案：使用信号量进行同步，使用互斥锁进行互斥
// 1. 需要2把锁：一把保护生产者，一把保护消费者
// 2. 锁的使用方式：生产者在插入数据前加锁，完成后解锁；消费者在取出数据前加锁，完成后解锁
template<typename T>
class RingQueue
{
private:
    // 信号量P操作封装：减少信号量值，若信号量为0则阻塞
    void P(sem_t &sem)
    {
        sem_wait(&sem);
    }
    
    // 信号量V操作封装：增加信号量值，唤醒等待该信号量的线程
    void V(sem_t &sem)
    {
        sem_post(&sem);
    }
    
    // 互斥锁加锁操作封装
    void Lock(pthread_mutex_t &mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    
    // 互斥锁解锁操作封装
    void Unlock(pthread_mutex_t &mutex)
    {
        pthread_mutex_unlock(&mutex);
    }
public:
    // 构造函数：初始化环形队列
    // 参数cap：环形队列的容量大小
    RingQueue(int cap): _ring_queue(cap), _cap(cap),  _productor_step(0), _consumer_step(0)
    {
        // 初始化信号量：_room_sem表示空槽位数量，初始值为队列容量
        sem_init(&_room_sem, 0, _cap);
        // 初始化信号量：_data_sem表示数据数量，初始值为0
        sem_init(&_data_sem, 0, 0);

        // 初始化生产者互斥锁
        pthread_mutex_init(&_productor_mutex, nullptr);
        // 初始化消费者互斥锁
        pthread_mutex_init(&_consumer_mutex, nullptr);
    }
    
    // 入队方法：将元素添加到环形队列
    // 参数in：要入队的元素
    void Enqueue(const T &in)
    {
        // 1. 等待空槽位（P操作）
        P(_room_sem);
        
        // 2. 加生产者锁，保证生产者之间的互斥
        Lock(_productor_mutex);
        
        // 3. 将元素加入队列
        _ring_queue[_productor_step++] = in; 
        // 4. 生产者索引循环更新
        _productor_step %= _cap;
        
        // 5. 解锁生产者锁
        Unlock(_productor_mutex);
        
        // 6. 增加数据信号量（V操作），通知消费者有新数据可用
        V(_data_sem);
    }
    
    // 出队方法：从环形队列取出元素
    // 参数out：用于存储出队元素的指针
    void Pop(T *out)
    {
        // 1. 等待数据（P操作）
        P(_data_sem);
        
        // 2. 加消费者锁，保证消费者之间的互斥
        Lock(_consumer_mutex);
        
        // 3. 取出元素
        *out = _ring_queue[_consumer_step++];
        // 4. 消费者索引循环更新
        _consumer_step %= _cap;
        
        // 5. 解锁消费者锁
        Unlock(_consumer_mutex);
        
        // 6. 增加空槽位信号量（V操作），通知生产者有新空间可用
        V(_room_sem);
    }
    // 析构函数：销毁信号量和互斥锁
    ~RingQueue()
    {
        // 销毁信号量
        sem_destroy(&_room_sem);
        sem_destroy(&_data_sem);

        // 销毁互斥锁
        pthread_mutex_destroy(&_productor_mutex);
        pthread_mutex_destroy(&_consumer_mutex);
    }
private:
    // 1. 环形队列的底层存储
    std::vector<T> _ring_queue;
    // 环形队列的容量上限
    int _cap;

    // 2. 生产和消费的当前位置索引
    // 生产者当前位置
    int _productor_step;
    // 消费者当前位置
    int _consumer_step;

    // 3. 同步用的信号量
    // 表示空槽位数量，生产者关心
    sem_t _room_sem;
    // 表示数据数量，消费者关心
    sem_t _data_sem;

    // 4. 互斥用的锁
    // 保护生产者操作的互斥锁
    pthread_mutex_t _productor_mutex;
    // 保护消费者操作的互斥锁
    pthread_mutex_t _consumer_mutex;
};